/************************************************************************************ 
 * Copyright (c) 2008-2011, Columbia University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Columbia University nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY COLUMBIA UNIVERSITY ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * 
 * ===================================================================================
 * Author: Ohan Oda (ohan@cs.columbia.edu)
 * 
 * -----------------------------------------------------------------------------------
 * Modified Version of Hristo Hristov for NOVA (11.08.11)
 * 
 * Revision 6
 *************************************************************************************/ 

using System;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.Xml;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using NOVA.ScreenManagement;
using BEPUphysics.BroadPhaseSystems;

namespace NOVA
{
    /// <summary>
    /// This class summerizes all important parameters of the NOVA framework.
    /// </summary>
    public class Core
    {
        #region Member Fields
        private static GraphicsDeviceManager _graphics;
        private static GraphicsDevice _device;
        private static ContentManager _content;
        private static bool initialized;
        private static Texture2D blankTexture;
        private static Matrix viewMatrix;
        private static Matrix projMatrix;
        private static Matrix viewProjMatrix;
        private static Matrix viewInverse;
        private static Matrix cameraTransform;

        private static IQueryAccelerator m_queryAccelerator;

        private static Game m_game;
        private static ScreenManager m_screenManager;
        private static ContentManager m_embeddedManager;

        private static Dictionary<string, string> settings;

        private static int nextNodeID;

        private static bool enableNetworking;
        private static bool isServer;
        private static int numWaitForClients;

        //private static ShadowMapShader shadowShader;
        private static Color boundingBoxColor;

        private static bool showFPS;
        private static bool showTriangleCount;
        private static bool showNotifications;
        private static bool showCameraPosition;
        private static Color debugTextColor;        

        private static ushort threadOption;
        #endregion

        #region Constructors

        /// <summary>
        /// This class summerizes all important parameters of the NOVA framework.
        /// </summary>
        static Core()
        {
            initialized = false;
        }
        #endregion

        private static void graphics_DeviceReset(object sender, EventArgs e)
        {
            // Re-Set device
            _device = _graphics.GraphicsDevice;

            // Restore z buffer state
            _device.DepthStencilState = DepthStencilState.Default;
            // Set u/v addressing back to wrap
            _device.SamplerStates[0] = SamplerState.LinearWrap;
        }

        #region Properties

        /// <summary>
        /// Gets or sets whether to display the current camera position on the screen.
        /// </summary>
        public static bool ShowCameraPosition
        {
            get { return showCameraPosition; }
            set { showCameraPosition = value; }
        }

        public static IQueryAccelerator QueryAccelerator
        {
            get { return m_queryAccelerator; }
            internal set { m_queryAccelerator = value; }
        }

        public static Game Game
        {
            get { return m_game; }            
        }

        public static ScreenManager ScreenManager
        {
            get { return m_screenManager; }
        }

        public static ContentManager EmbeddedContent
        {
            get { return m_embeddedManager; }
        }

        public static GraphicsDeviceManager Graphics
        {
            get { return _graphics; }
        }

        public static GraphicsDevice Device
        {
            get { return _device; }
            set { _device = value; } // INTERVENTION
        }

        public static ContentManager Content
        {
            get { return _content; }
            set { _content = value; }
        }

        /// <summary>
        /// Gets the width dimension of the current screen in pixels
        /// </summary>
        public static int Width
        {
            get { return _device.Viewport.Width; }
        }

        /// <summary>
        /// Gets the height dimension of the current screen in pixels
        /// </summary>
        public static int Height
        {
            get { return _device.Viewport.Height; }
        }

        /// <summary>
        /// Gets whether Goblin framework has been initialized through the InitGoblin method call
        /// </summary>
        public static bool Initialized
        {
            get { return initialized; }
        }

        /// <summary>
        /// Gets a blank texture
        /// </summary>
        public static Texture2D BlankTexture
        {
            get { return blankTexture; }
        }

        public static Matrix ViewMatrix
        {
            get { return viewMatrix; }
             set 
            { 
                viewMatrix = value;
                viewInverse = Matrix.Invert(viewMatrix);
            }
        }

        public static Matrix ProjectionMatrix
        {
            get { return projMatrix; }
            set { projMatrix = value; }
        }

        public static Matrix ViewProjectionMatrix
        {
            get { return viewProjMatrix; }
            internal set { viewProjMatrix = value; }
        }

        public static Matrix ViewInverseMatrix
        {
            get { return viewInverse; }
        }

        public static Matrix CameraTransform
        {
            get { return cameraTransform; }
            set { cameraTransform = value; }
        }

        /// <summary>
        /// Gets or sets whether to enable alpha-blended transparency
        /// </summary>
        public static bool AlphaBlendingEnabled
        {
            set
            {
                if (value)
                {
                    _device.BlendState = BlendState.AlphaBlend;
                }
                else
                    _device.BlendState = BlendState.Opaque;
            }
        }

        /// <summary>
        /// Gets or sets the threading options by oring the ThreadOptions enum. This property is
        /// used to control what operations to be threaded. If your CPU is single-core, then there is
        /// no point to thread any operations.
        /// </summary>
        public static ushort ThreadOption
        {
            get { return threadOption; }
            set { threadOption = value; }
        }

        /// <summary>
        /// Gets the shader used to perform shadow mapping
        /// </summary>
        /*public static ShadowMapShader ShadowShader
        {
            get { return shadowShader; }
            set { shadowShader = value; }
        }*/

        /// <summary>
        /// Gets or sets the color used to draw the bounding box of each model for debugging.
        /// </summary>
        public static Color BoundingBoxColor
        {
            get { return boundingBoxColor; }
            set { boundingBoxColor = value; }
        }

        /// <summary>
        /// Gets or sets whether to enable networking. The default value is false.
        /// </summary>
        public static bool EnableNetworking
        {
            get { return enableNetworking; }
            set { enableNetworking = value; }
        }

        /// <summary>
        /// Gets or sets whether this machine acts as a server when networking is enabled
        /// </summary>
        public static bool IsServer
        {
            get { return isServer; }
            set { isServer = value; }
        }

        /// <summary>
        /// Number of clients to wait for connections before starting physics simulation.
        /// The default value is 0.
        /// </summary>
        public static int NumberOfClientsToWait
        {
            get { return numWaitForClients; }
            set { numWaitForClients = value; }
        }

        /// <summary>
        /// Gets or sets whether to display the Frames-Per-Secound count on the screen.
        /// </summary>
        /// <remarks>
        /// The color of the text can be changed by modifying DebutTextColor property. 
        /// </remarks>
        /// <see cref="DebugTextColor"/>
        public static bool ShowFPS
        {
            get { return showFPS; }
            set { showFPS = value; }
        }

        /// <summary>
        /// Gets the current FPS count.
        /// </summary>
        public static int FPS
        {
            get;
            internal set;
        }

        /// <summary>
        /// Gets or sets whether to display any notification messages on the screen.
        /// </summary>
        public static bool ShowNotifications
        {
            get { return showNotifications; }
            set { showNotifications = value; }
        }

        /// <summary>
        /// Gets or sets whether to display the triangle count of all of the rendered 
        /// models in the scene.
        /// </summary>
        /// <remarks>
        /// The color of the text can be changed by modifying DebutTextColor property. 
        /// </remarks>
        /// <see cref="DebugTextColor"/>
        public static bool ShowTriangleCount
        {
            get { return showTriangleCount; }
            set { showTriangleCount = value; }
        }

        /// <summary>
        /// Gets or sets the color of the FPS or triangle count text. The default color 
        /// is Color.White.
        /// </summary>
        public static Color DebugTextColor
        {
            get { return debugTextColor; }
            set { debugTextColor = value; }
        }

        #endregion

        #region Public Static Methods
        /// <summary>
        /// This is the very first method that needs to be called before using any of the NOVA
        /// XNA framework.
        /// </summary>
        public static void Init(Game game, GraphicsDeviceManager graphics, ContentManager content, ScreenManager screenManager)
        {
            if (game == null || graphics == null || content == null || screenManager == null)
                throw new Exception("Arguments can not be null");

            m_game = game;
            m_screenManager = screenManager;
            _graphics = graphics;
            _device = graphics.GraphicsDevice;
            _graphics.DeviceReset += new EventHandler<EventArgs>(graphics_DeviceReset);
            _content = content;
            initialized = true;
            // creates a blank texture for 2D primitive drawing when texture is not needed
            blankTexture = new Texture2D(_device, 1, 1, false, SurfaceFormat.Bgra5551);

            m_embeddedManager = new ResourceContentManager(game.Services, Resources.ResourceManager);

            // puts a white pixel in this blank texture to make a 1x1 blank texture
            ushort[] texData = new ushort[1];
            texData[0] = (ushort)0xFFFF;
            blankTexture.SetData(texData);
            viewMatrix = Matrix.Identity;
            projMatrix = Matrix.Identity;
              
            nextNodeID = 0;

            showFPS = false;
            showNotifications = false;
            showTriangleCount = false;
            debugTextColor = Color.White;

            enableNetworking = false;
            isServer = false;
            numWaitForClients = 0;

            cameraTransform = Matrix.Identity;
        }

        /// <summary>
        /// Gets the setting variables loaded at the initialization time.
        /// </summary>
        /// <param name="name">The name of the setting variable</param>
        /// <returns></returns>
        public static String GetSettingVariable(String name)
        {
            if (!settings.ContainsKey(name))
                return "";
            else
                return (String)settings[name];
        }
        #endregion
        
        /// <summary>
        /// A helper function to get the next unique node ID.
        /// </summary>
        /// <returns></returns>
        internal static int GetNextNodeID()
        {
            nextNodeID++;
            return nextNodeID;
        }

        internal static void Restore3DSettings()
        {
            //State.Device.SamplerStates[0] = SamplerState.LinearWrap;
            //State.Device.Textures[0] = null;
        }
    }
}
